import type { Mapless } from '..';
import type { IdHubType } from '../core/IdHub';
import IdHub from '../core/IdHub';
import { Manager } from '../core/manager';
import {
  calculateEndPoint,
  calculateNextPoint,
  getOption,
  getTextOffsetDistance,
  toNumber,
} from '../core/utils';
import type { Line, LineManagerOptions } from '../index.d';

type LineIdHubType = IdHubType & {
  lineSourceId: string;
  lineLayerId: string;
  textSourceId: string;
  textLayerId: string;
};

class LineManager extends Manager<LineIdHubType> {
  private lines: Record<string, Line> = {};
  private showText = false;

  constructor(mapless: Mapless, options?: LineManagerOptions) {
    super(mapless, options, 'line');
    this.onInitialized();
    // 设置是否显示文字，默认为false
    this.showText = getOption<boolean>('showText', options, false);
  }

  override createIdHub(_idHub?: IdHubType) {
    const newHub = new IdHub({
      layers: {
        lineLayerId: 'dzmap-line-layer',
        textLayerId: 'dzmap-line-text-layer',
      },
      sources: {
        lineSourceId: 'dzmap-line-source',
        textSourceId: 'dzmap-line-text-source',
      },
    }) as LineIdHubType;
    super.createIdHub(newHub);
  }

  override initLayers() {
    if (!this.mapless) return;
    const sid = this.idHub?.lineSourceId;
    this.mapless.addEmptySource(sid);
    this.mapless.addLayer({
      id: this.idHub?.lineLayerId,
      type: 'line',
      source: sid,
      paint: {
        'line-color': ['get', 'color'],
        'line-width': 3,
      },
    });
    this.activeBeforeManager();

    const tid = this.idHub?.textSourceId;
    this.mapless.addEmptySource(tid);
    this.mapless.addLayer({
      id: this.idHub?.textLayerId,
      type: 'symbol',
      source: tid,
      layout: {
        'text-field': ['get', 'text'],
        'text-size': 14,
        'text-offset': [0, 0],
        'text-allow-overlap': true, // 允许文字重叠
        'text-ignore-placement': true, // 忽略文字位置冲突
        'text-rotate': ['get', 'angle'],
      },
      paint: {
        'text-color': this.mapless.isDark()
          ? 'rgba(255, 255, 255, 0.8)'
          : '#202020',
        'text-halo-color': this.mapless.isDark()
          ? 'rgba(255, 255, 255, 0.3)'
          : 'rgba(0, 0, 0, 0.3)',
        'text-halo-width': 1, // 阴影宽度
        'text-halo-blur': 0, // 阴影模糊度
      },
    });
  }

  override onStyleChanged = (tag: string) => {
    if (this.idHub?.textLayerId) {
      this.mapless?.map?.setPaintProperty(
        this.idHub?.textLayerId,
        'text-color',
        this.mapless.isDark(tag) ? 'rgba(255, 255, 255, 0.8)' : '#202020',
      );
      this.mapless?.map?.setPaintProperty(
        this.idHub?.textLayerId,
        'text-halo-color',
        this.mapless.isDark(tag)
          ? 'rgba(0, 0, 0, 0.3)'
          : 'rgba(255, 255, 255, 0.3)',
      );
    }
  };

  private _updateDraw() {
    const lines = Object.values(this.lines);
    // 为每条线创建GeoJSON数据
    const features = lines.map((line) => {
      const endPoint = calculateEndPoint(line);
      const lng = toNumber(line.longitude);
      const lat = toNumber(line.latitude);
      return {
        type: 'Feature' as const,
        geometry: {
          type: 'LineString' as const,
          coordinates: [
            [lng, lat], // 起点
            endPoint, // 终点
          ],
        },
        properties: {
          id: line.id,
          text: `${line.azimuth || '-'}°`,
          color: line.color || '#3bb2d0',
        },
      };
    });
    this.mapless?.setData(this.idHub?.lineSourceId, {
      type: 'FeatureCollection',
      features,
    });
    if (this.showText) {
      const textFeatures = lines.map((line) => ({
        type: 'Feature' as const,
        geometry: {
          type: 'Point' as const,
          coordinates: calculateNextPoint(
            [Number(line.longitude), Number(line.latitude)],
            line.azimuth || 0,
            getTextOffsetDistance(this.map?.getZoom()),
          ),
        },
        properties: {
          id: `${line.id}-text`,
          text: line.azimuth !== undefined ? `${line.azimuth}°` : '',
          angle: (line.azimuth || 0) - 90,
        },
      })) as GeoJSON.Feature[];
      this.mapless?.setData(this.idHub?.textSourceId, {
        type: 'FeatureCollection',
        features: textFeatures,
      });
    }
  }

  drawLines(lines: Line[], clear = false) {
    if (clear) {
      this.clearLines(false);
    }
    // 缓存线条数据
    lines.forEach((line) => {
      this.lines[line.id] = line;
    });
    this._updateDraw();
  }

  updateLine(
    options:
      | {
          id: string;
          longitude?: number;
          latitude?: number;
          azimuth?: number;
          lineLength?: number;
          color?: string;
        }
      | Partial<Line>,
    redraw = true,
  ) {
    if (!options.id) return;
    if (!this.lines[options.id]) return;
    // 更新缓存数据
    const line = this.lines[options.id];
    if (options.longitude !== undefined) line.longitude = options.longitude;
    if (options.latitude !== undefined) line.latitude = options.latitude;
    if (options.azimuth !== undefined) line.azimuth = options.azimuth;
    if (options.lineLength !== undefined) line.lineLength = options.lineLength;
    if (options.color !== undefined) line.color = options.color;
    // 更新数据源
    redraw && this._updateDraw();
  }

  updateLines(lines: Line[]) {
    lines.forEach((line) => {
      this.updateLine(line, false);
    });
    this._updateDraw();
  }

  updatePositions(lngLat: [number, number]) {
    Object.values(this.lines).forEach((line) => {
      this.updateLine(
        { id: line.id, longitude: lngLat[0], latitude: lngLat[1] },
        false,
      );
    });
    this._updateDraw();
  }

  clearLines(redraw = true) {
    this.lines = {};
    redraw && this._updateDraw();
  }

  removeLine(id: string) {
    delete this.lines[id];
    this._updateDraw();
  }

  override destroy = () => {
    super.destroy();
    this.lines = {};
  };
}

export default LineManager;

export const attachLine = (
  mapless: Mapless | undefined,
  name?: string,
  options?: LineManagerOptions,
) => mapless?.use(LineManager, name, options);
export const getLine = (mapless: Mapless | undefined, name?: string) =>
  mapless?.get(LineManager, name);
export const detachLine = (mapless: Mapless | undefined, name?: string) =>
  mapless?.unuse(LineManager, name);
